asynx.namespace("anim");
asynx.namespace("anim.easing");
asynx.namespace("anim.tween");

asynx.anim.tween = function() {
	this.init(arguments);
};

asynx.anim.tween.prototype = {
	
	init: function(arguments) {
		this.el = asynx(arguments[0]);
		this.begin = arguments[1];
		this.finish = arguments[2];
		this.duration = arguments[3];
		this.eq = arguments[4]||asynx.anim.easing.linear;
		this.s = arguments[5];
		this.p = arguments[6];
		this.change = this.finish-this.begin;
		this.time = 0;
		this.frame = null;
		this.animate();
	},
	
	animate: function() {
		var scope = this;

		var render = function() {
			var pos = this.eq.apply(this, [this.time++, this.begin, this.change, this.duration, this.s, this.p]);
			
			if (this.onTweenUpdate)
				this.onTweenUpdate.apply(this, [this.el, pos]);
				
			if (this.time > this.duration) {
				clearInterval(this.frame);
				if (this.onTweenEnd)
					this.onTweenEnd.apply(this, [this.el, pos]);
			};
		};
		
		this.frame = setInterval(function(){
			render.apply(scope);
		}, this.duration);
		
	},
	
	stop: function() {
		clearInterval(this.frame);
	}
	
};

asynx.anim.stop = function(tws) {
	if (!tws) return false;
	asynx.each(tws, function(tw) {
		if (tw) {
			clearInterval(tw.frame);
			delete tw;
		};
	});
};

asynx.anim.fade = function(el, a) {
	el = asynx(el);
	
	if (asynx.isArray(el) && !el.length) return false;
	
	var fn = arguments[2];
	var args = arguments[3];
	var duration = !asynx.isUndef(a.duration)?a.duration:20;
	var fac = asynx.anim.easing.factory;
	var equation = fac(a.equation)||fac('linear');
	var tweens = [];
	
	asynx.each(el, function(element) {
		var op = asynx(el).css("opacity");
		op = asynx.isEmpty(op) ? 100 : (!asynx.isIE?parseFloat(op)*100:op);
		var from = !asynx.isUndef(a.from)?a.from:op; 
		var to = !asynx.isUndef(a.to)?a.to:op;
		var tw = new asynx.anim.tween(element, from, to, duration, equation);
		tweens.push(tw);
		
		tw.onTweenUpdate = function(target, pos) {
			target.css({ "alpha": pos, visibility:'visible' });
		};
		tw.onTweenEnd = function(target, pos) {
			if (fn) fn.apply(this, [target, args]);
		};
	}, this);
	
	return tweens;
};

asynx.anim.move = function(el) {
	el = asynx(el);
	
	if (el.constructor == Array &&
		el.length==0) return false;
	
	var a = arguments[1]||{};
	var axis = arguments[2]||"x";
	var fn = arguments[3];
	var args = arguments[4];
	var fac = asynx.anim.easing.factory;
	var duration = a.duration||20;
	var equation = fac(a.equation)||fac('linear');
	var tweens = [];
	
	asynx.each(el, function(element) {
		var co = asynx.getXY(element);
		var def = co[axis=="x"?0:1];
		var from = !asynx.isUndef(a.from)?a.from:def; 
		var to = !asynx.isUndef(a.to)?a.to:def;
		element.css({position:'absolute'});
		
		var tw = new asynx.anim.tween(element, from, to, duration, equation);
		tweens.push(tw);
		
		tw.onTweenUpdate = function(target, pos) {
			if (axis=="x") target.css({ "left": pos });
			if (axis=="y") target.css({ "top": pos });
		};
		tw.onTweenEnd = function(target, pos) {
			if (fn) fn.apply(this, [target, args]);
		};
	}, this);
	
	return tweens;
};

asynx.anim.show = function(el) {
	el = asynx(el);
	
	if (el.constructor == Array &&
		el.length==0) return false;
	
	var a = arguments[1]||{};
	var axis = arguments[2]||"y";
	var fn = arguments[3];
	var args = arguments[4];
	var duration = a.duration||20;
	var fac = asynx.anim.easing.factory;
	var equation = fac(a.equation)||fac('bounceOut');
	var tweens = [];
	
	asynx.each(el, function(element) {
		var w = element.css("width");
		var h = element.css("height");
		var to = asynx.stripUnit(axis == "x" ? w : h);
		var tw = new asynx.anim.tween(element, 0, to, duration, equation);
		tweens.push(tw);
		
		tw.onTweenUpdate = function(ele, pos) {
			pos = asynx.stripUnit(pos) + 'px';
			if (axis=="x") ele.css({ "width": pos } );
			if (axis=="y") ele.css({ "height": pos } );
			el.css({visibility:'visible', display:'block'});
		};
		tw.onTweenEnd = function(ele, pos) {
			if (fn) fn.apply(this, [ele, args]);
		};
		
	}, this);

	return tweens;
};

asynx.anim.splash = function(el) {
	el = asynx(el);
	var a = arguments[1]||{};
	var fn = arguments[2];
	var args = arguments[3];
	var d = a.duration;
	el.css({opacity:0});
	return [ asynx.anim.show(el, { duration: d, equation:'strongInOut' }, "x"),
			  asynx.anim.show(el, { duration: d, equation:'strongInOut' }, "y"),
			  asynx.anim.fade(el, {from:0, to:100}, function(el, pos) {
			 	if (fn) fn.apply(this, [el, args]);
			  }) ];
};

asynx.anim.splashElastic = function(el) {
	el = asynx(el);
	var a = arguments[1]||{};
	var fn = arguments[2];
	var args = arguments[3];
	var d = a.duration;
	var tws = [];
	
	el.css({opacity:0});
	asynx.each(el, function(ele) {
		var from = ele.css("top")+50;
		tws.push(asynx.anim.splash(ele, { duration: d, equation:'linear' }));
		tws.push(asynx.anim.move(ele, { from: from , duration:d, equation: 'bounceOut' }, "y", function(el, pos) {
			 	if (fn) fn.apply(this, [el, args]);
			  }));
	}, this);
	
	return tws;
};

asynx.anim.easing = {
	
	factory: function(name) {

		if (asynx.isObject(name))
			return name;

		var linear = asynx.anim.easing.linear;
		if (!name) return linear;
		var m = name.match(/^(linear|normal|strong|expo|back|bounce|elastic)(InOut|In|Out)$/);
		if (!m) return false;
		var eq = asynx.anim.easing[m[1]][m[2]];
		return eq;
	},
	
	linear: function(t, b, c, d) {
		return c*t/d + b;
	},
	
	normal: {
		In: function(t, b, c, d) {
			return c*(t/=d)*t + b;
		},
		
		Out: function(t, b, c, d) {
			return -c *(t/=d)*(t-2) + b;
		},
		
		InOut: function(t, b, c, d) {
			if ((t/=d/2) < 1) return c/2*t*t + b;
			return -c/2 * ((--t)*(t-2) - 1) + b;
		}
	},
	
	strong/*quint*/: {
		In: function(t, b, c, d) {
			return c*(t/=d)*t*t*t*t + b;
		},
		
		Out: function(t, b, c, d) {
			return c*((t=t/d-1)*t*t*t*t + 1) + b;
		},
		
		InOut: function(t, b, c, d) {
			if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
			return c/2*((t-=2)*t*t*t*t + 2) + b;
		}
	},
	
	expo: {
		In: function(t, b, c, d) {
			return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
		},
		
		Out: function(t, b, c, d) {
			return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
		},
		
		InOut: function(t, b, c, d) {
			if (t==0) return b;
			if (t==d) return b+c;
			if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
			return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
		}
	},
	
	back: {
		In: function(t, b, c, d, s) {
			s = s?s:1.70158;
			return c*(t/=d)*t*((s+1)*t - s) + b;
		},
		
		Out: function(t, b, c, d, s) {
			s = s?s:1.70158;
			return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
		},
		
		InOut: function(t, b, c, d, s) {
			s = s?s:1.70158;
			if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
			return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
		}
	},
	
	bounce: {
		In: function(t, b, c, d, s) {
			return c - asynx.anim.easing.bounce.Out (d-t, 0, c, d) + b;
		},
		
		Out: function(t, b, c, d, s) {
			if ((t/=d) < (1/2.75)) {
				return c*(7.5625*t*t) + b;
			} else if (t < (2/2.75)) {
				return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
			} else if (t < (2.5/2.75)) {
				return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
			} else {
				return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
			}
		},
		
		InOut: function(t, b, c, d, s) {
			if (t < d/2) return asynx.anim.easing.bounce.In (t*2, 0, c, d) * 0.5 + b;
			else return asynx.anim.easing.bounce.Out (t*2-d, 0, c, d) * 0.5 + c*0.5 + b;
		}
	},
	
	elastic: {
		In: function(t, b, c, d, a, p) {
			if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*0.3;
			if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
			else var s = p/(2*Math.PI) * Math.asin (c/a);
			return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
		},
		
		Out: function(t, b, c, d, a, p) {
			if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*0.3;
			if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
			else var s = p/(2*Math.PI) * Math.asin (c/a);
			return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b);
		},
		
		InOut: function(t, b, c, d, a, p) {
			if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(0.3*1.5);
			if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
			else var s = p/(2*Math.PI) * Math.asin (c/a);
			if (t < 1) return -0.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
			return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*0.5 + c + b;
		}
	}
	
};
